#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <alsa/asoundlib.h>
#include <opus/opus.h>
#include <ogg/ogg.h>
#include <cctype>

// 配置参数
#define PCM_BUFFER_SIZE 12000  // 容纳Opus最大帧(5760)的缓冲区
#define OGG_BUFFER_SIZE 4096   // Ogg包读取缓冲区
#define MAX_LOOP_ITERATIONS 1000  // 最大循环迭代次数
static const unsigned int ALSA_BUFFER_TIME = 500000;  // ALSA缓冲区大小(500ms)
static const unsigned int ALSA_PERIOD_TIME = 100000;  // ALSA周期大小(100ms)

// OpusHead结构体定义(用于解析文件头)
#pragma pack(push, 1)
typedef struct {
    char magic[8];       // "OpusHead"标识
    uint8_t version;     // 版本号
    uint8_t channels;    // 声道数
    uint16_t pre_skip;   // 预跳过采样数
    uint32_t sample_rate;// 采样率
    uint16_t gain;       // 增益
    uint8_t channel_map; // 声道映射
} OpusHead;
#pragma pack(pop)

// 小端字节序转换函数
static uint16_t le16(uint16_t val) {
    return (val & 0x00FF) << 8 | (val & 0xFF00) >> 8;
}

static uint32_t le32(uint32_t val) {
    return (val & 0x000000FF) << 24 |
           (val & 0x0000FF00) << 8  |
           (val & 0x00FF0000) >> 8  |
           (val & 0xFF000000) >> 24;
}

// 初始化ALSA音频设备
static int init_alsa(snd_pcm_t **pcm_handle, int sample_rate, int channels) {
    int rc;
    
    // 打开PCM设备
    rc = snd_pcm_open(pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
    if (rc < 0) {
        fprintf(stderr, "无法打开PCM设备: %s\n", snd_strerror(rc));
        return -1;
    }

    // 配置PCM参数
    snd_pcm_hw_params_t *params;
    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(*pcm_handle, params);

    // 设置访问模式(交错模式)
    rc = snd_pcm_hw_params_set_access(*pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    if (rc < 0) {
        fprintf(stderr, "设置访问模式失败: %s\n", snd_strerror(rc));
        return -1;
    }

    // 设置音频格式(16位小端)
    rc = snd_pcm_hw_params_set_format(*pcm_handle, params, SND_PCM_FORMAT_S16_LE);
    if (rc < 0) {
        fprintf(stderr, "设置格式失败: %s\n", snd_strerror(rc));
        return -1;
    }

    // 设置声道数
    rc = snd_pcm_hw_params_set_channels(*pcm_handle, params, channels);
    if (rc < 0) {
        fprintf(stderr, "设置声道数失败: %s\n", snd_strerror(rc));
        return -1;
    }

    // 设置采样率
    unsigned int rate = sample_rate;
    int dir = 0;
    rc = snd_pcm_hw_params_set_rate_near(*pcm_handle, params, &rate, &dir);
    if (rc < 0) {
        fprintf(stderr, "设置采样率失败: %s\n", snd_strerror(rc));
        return -1;
    }
    if (rate != sample_rate) {
        fprintf(stderr, "警告: 无法设置采样率为%d, 实际使用%d\n", sample_rate, rate);
    }

    // 设置缓冲区时间
    unsigned int buffer_time = ALSA_BUFFER_TIME;
    rc = snd_pcm_hw_params_set_buffer_time_near(*pcm_handle, params, &buffer_time, &dir);
    if (rc < 0) {
        fprintf(stderr, "设置缓冲区时间失败: %s\n", snd_strerror(rc));
        return -1;
    }

    // 设置周期时间
    unsigned int period_time = ALSA_PERIOD_TIME;
    rc = snd_pcm_hw_params_set_period_time_near(*pcm_handle, params, &period_time, &dir);
    if (rc < 0) {
        fprintf(stderr, "设置周期时间失败: %s\n", snd_strerror(rc));
        return -1;
    }

    // 应用参数
    rc = snd_pcm_hw_params(*pcm_handle, params);
    if (rc < 0) {
        fprintf(stderr, "应用PCM参数失败: %s\n", snd_strerror(rc));
        return -1;
    }

    return 0;
}

// 播放PCM数据
static int play_pcm(snd_pcm_t *pcm_handle, int16_t *pcm_data, int frame_count, int channels) {
    if (frame_count <= 0) return 0;

    int rc;
    int frames_to_write = frame_count;
    int16_t *ptr = pcm_data;

    while (frames_to_write > 0) {
        rc = snd_pcm_writei(pcm_handle, ptr, frames_to_write);
        if (rc < 0) {
            // 尝试恢复错误(如underrun)
            rc = snd_pcm_recover(pcm_handle, rc, 0);
            if (rc < 0) {
                fprintf(stderr, "播放错误: %s\n", snd_strerror(rc));
                return -1;
            }
            continue;
        }
        ptr += rc * channels;
        frames_to_write -= rc;
    }

    return 0;
}

// 播放Opus文件的主函数
int PlayOpusFile(const char *file_path) {
    printf("播放文件: %s\n", file_path);

    // 打开文件
    FILE *file = fopen(file_path, "rb");
    if (!file) {
        fprintf(stderr, "无法打开文件: %s\n", file_path);
        return -1;
    }

    // Ogg相关结构体初始化
    ogg_sync_state oy;
    ogg_stream_state os;
    ogg_page og;
    ogg_packet op;
    ogg_sync_init(&oy);

    // 解析OpusHead需要的变量
    int serialno = -1;
    int found = 0;
    int loop_count = 0;
    int channels = 1;          // 默认单声道
    const int decoder_sample_rate = 48000; // Opus推荐解码采样率

    // 寻找OpusHead头部信息
    while (!found && loop_count < MAX_LOOP_ITERATIONS) {
        loop_count++;
        char *buffer = ogg_sync_buffer(&oy, OGG_BUFFER_SIZE);
        int bytes = fread(buffer, 1, OGG_BUFFER_SIZE, file);
        if (bytes == 0) {
            fprintf(stderr, "文件结束但未找到Opus流\n");
            goto cleanup;
        }
        ogg_sync_wrote(&oy, bytes);

        // 处理Ogg页面
        while (ogg_sync_pageout(&oy, &og) > 0) {
            if (serialno == -1) {
                serialno = ogg_page_serialno(&og);
                ogg_stream_init(&os, serialno);
                printf("找到Ogg流, serialno: %d\n", serialno);
            }
            if (ogg_page_serialno(&og) == serialno) {
                ogg_stream_pagein(&os, &og);
                
                // 处理Ogg包
                while (ogg_stream_packetout(&os, &op) > 0) {
                    // 检查是否为OpusHead
                    if (op.packet && op.bytes >= sizeof(OpusHead) && 
                        memcmp(op.packet, "OpusHead", 8) == 0) {
                        OpusHead *head = (OpusHead *)op.packet;
                        channels = head->channels;
                        int file_sample_rate = le32(head->sample_rate);
                        printf("解析到音频参数: 声道数=%d, 文件采样率=%dHz\n", 
                               channels, file_sample_rate);
                        
                        if (channels < 1 || channels > 2) {
                            fprintf(stderr, "无效声道数: %d\n", channels);
                            goto cleanup;
                        }
                        found = 1;
                        break;
                    }
                }
            }
            if (found) break;
        }
        if (found) break;
    }

    if (!found) {
        fprintf(stderr, "未找到OpusHead, 不是有效的Opus文件\n");
        goto cleanup;
    }

    // 用代码块包裹后续变量, 避免goto跨越初始化
    {
        // 跳过OpusTags包
        int found_tags = 0;
        while (ogg_stream_packetout(&os, &op) > 0) {
            if (op.packet && op.bytes >= 8 && memcmp(op.packet, "OpusTags", 8) == 0) {
                printf("跳过OpusTags包\n");
                found_tags = 1;
                break;
            }
        }

        // 初始化解码器
        int error;
        OpusDecoder *decoder = opus_decoder_create(decoder_sample_rate, channels, &error);
        if (error != OPUS_OK || !decoder) {
            fprintf(stderr, "创建解码器失败: %s\n", opus_strerror(error));
            goto cleanup;
        }

        // 初始化ALSA设备
        snd_pcm_t *pcm_handle = NULL;
        if (init_alsa(&pcm_handle, decoder_sample_rate, channels) != 0) {
            fprintf(stderr, "ALSA初始化失败\n");
            opus_decoder_destroy(decoder);
            goto cleanup;
        }

        // 分配PCM缓冲区(动态分配避免栈溢出)
        int16_t *pcm_buffer = (int16_t*)malloc(PCM_BUFFER_SIZE * channels * sizeof(int16_t));
        if (!pcm_buffer) {
            fprintf(stderr, "内存分配失败\n");
            opus_decoder_destroy(decoder);
            snd_pcm_close(pcm_handle);
            goto cleanup;
        }

        // 解码播放循环变量
        int eof = 0;
        int skip_count = 0;
        const int MAX_SKIPS = 10;
        int total_frames = 0;

        printf("开始播放...\n");
        while (!eof && skip_count < MAX_SKIPS) {
            int result = ogg_stream_packetout(&os, &op);
            if (result < 0) {
                fprintf(stderr, "Ogg包错误, 跳过(%d)\n", skip_count++);
                continue;
            }

            if (result == 0) {
                // 没有可用包, 读取更多数据
                if (feof(file)) {
                    eof = 1;
                    break;
                }
                char *buffer = ogg_sync_buffer(&oy, OGG_BUFFER_SIZE);
                int bytes = fread(buffer, 1, OGG_BUFFER_SIZE, file);
                if (bytes == 0) {
                    eof = 1;
                    break;
                }
                ogg_sync_wrote(&oy, bytes);
                
                // 处理新页面
                while (ogg_sync_pageout(&oy, &og) > 0) {
                    if (ogg_page_serialno(&og) == serialno) {
                        ogg_stream_pagein(&os, &og);
                    }
                }
                continue;
            }

            // 跳过BOS包
            if (op.b_o_s) continue;

            // 解码Opus包
            int frame_count = opus_decode(
                decoder,
                op.packet,
                op.bytes,
                pcm_buffer,
                PCM_BUFFER_SIZE,
                0
            );

            if (frame_count < 0) {
                fprintf(stderr, "解码错误: %s, 跳过(%d)\n", opus_strerror(frame_count), skip_count++);
                continue;
            }

            // 播放PCM数据
            if (frame_count > 0) {
                total_frames += frame_count;
                if (play_pcm(pcm_handle, pcm_buffer, frame_count, channels) != 0) {
                    fprintf(stderr, "播放失败, 中断\n");
                    break;
                }
            }

            // 检查流结束
            if (op.e_o_s) {
                eof = 1;
                break;
            }
        }

        printf("播放完成, 总帧数: %d\n", total_frames);

        // 释放当前代码块内的资源
        free(pcm_buffer);
        opus_decoder_destroy(decoder);
        if (pcm_handle) {
            snd_pcm_drain(pcm_handle);
            snd_pcm_close(pcm_handle);
        }
    }

cleanup:
    // 清理外部资源
    if (serialno != -1) ogg_stream_clear(&os);
    ogg_sync_clear(&oy);
    if (file) fclose(file);
    return 0;
}

